package com.nas.app;

import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.io.Reader;
import java.io.Writer;
import java.util.Date;
import java.util.Dictionary;
import java.util.Enumeration;
import java.util.Hashtable;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.PriorityBlockingQueue;

import com.nas.app.process.ThreadControl;
import com.nas.app.process.ThreadControllable;
import com.nas.app.process.ThreadUniqueId;

public class ChatCli implements ThreadControllable, Runnable {

    private static String CONSOLE_IN = "Console-in";
    private static String CONSOLE_OUT = "Console-out";

    private BlockingQueue<Message> outMessageQueue;
    private BlockingQueue<Message> inMessageQueue;
    private String appThreadName;
    private Long appThreadId;

    private Long consoleOutThreadId;
    private Long consoleInThreadId;

    private Dictionary<Long, ThreadControl> threads;

    public ChatCli(BlockingQueue<Message> outMessageQueue) {

        this.outMessageQueue = outMessageQueue;
        this.inMessageQueue = new PriorityBlockingQueue<Message>();
        // In
        ReaderConvert readerConvert = new ReaderConvert(this.inMessageQueue, new InputStreamReader(System.in));
        readerConvert.setAppThreadName(CONSOLE_IN);
        readerConvert.setAppThreadId(ThreadUniqueId.get());
        consoleInThreadId = readerConvert.getAppThreadId();

        Thread readerConvertThread = new Thread(readerConvert);
        ThreadControl threadControlReader = new ThreadControl();
        threadControlReader.setThreadControllable(readerConvert);
        threadControlReader.setThread(readerConvertThread);
        threadControlReader.setInQueue(null);
        threadControlReader.setOutQueue(this.inMessageQueue);

        // Out
        BlockingQueue<Message> outToConsoleMessageQueue = new PriorityBlockingQueue<Message>();
        WriterConvert writerConvert = new WriterConvert(outToConsoleMessageQueue, new OutputStreamWriter(System.out));
        writerConvert.setAppThreadName(CONSOLE_OUT);
        writerConvert.setAppThreadId(ThreadUniqueId.get());
        consoleOutThreadId = writerConvert.getAppThreadId();

        Thread writerConvertThread = new Thread(writerConvert);
        ThreadControl threadControlWriter = new ThreadControl();
        threadControlWriter.setThreadControllable(writerConvert);
        threadControlWriter.setThread(writerConvertThread);
        threadControlWriter.setInQueue(null);
        threadControlWriter.setOutQueue(outToConsoleMessageQueue);

        threads = new Hashtable<Long, ThreadControl>();
        System.out.println("readerConvert.getId(): " + readerConvert.getAppThreadId());
        System.out.println("writerConvert.getId(): " + writerConvert.getAppThreadId());
        threads.put(readerConvert.getAppThreadId(), threadControlReader);
        threads.put(writerConvert.getAppThreadId(), threadControlWriter);
    }

    @Override
    public void run() {
        for (Enumeration<ThreadControl> elements = threads.elements(); elements.hasMoreElements();) {
            elements.nextElement().getThread().start();
        }
        try {
            while (true) {
                loadMessage(inMessageQueue.take());
            }
        } catch (InterruptedException ex) {
            System.out.println("ChatCli-Exception: " + ex);
        }

    }

    public Long getAppThreadId() {
        return appThreadId;
    }

    public void setAppThreadId(Long id) {
        this.appThreadId = id;
    }

    private void loadMessage(Message message) {
        ThreadControl threadControl = null;
        try {
            if ((message.getToThreadId() == null) || (message.getToThreadId() == this.appThreadId)) {
                // Es para mi
                if (message.getToThreadId() == null) {
                    // Es de un hijo.
                    threadControl = threads.get(message.getFromThreadId());
                    if (CONSOLE_IN.equals(threadControl.getThreadControllable().getAppThreadName())) {
                        message.setThreadId(this.appThreadId);
                        message.setType(Message.Type.TASK);
                        this.outMessageQueue.put(message);
                    }

                } else {
                    // Padre
                    if (message.getFromThreadId() == null) {
                        threadControl = threads.get(consoleOutThreadId);
                        threadControl.getOutQueue().put(message);
                    } else {
                        // Externo.

                    }
                }
            } else {
                threadControl = threads.get(message.getToThreadId());
                if (threadControl != null) {
                    threadControl.getInQueue().put(message);
                } else {
                    System.out.println("Mensaje perdido");
                }
            }
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

    }

    public BlockingQueue<Message> getOutMessageQueue() {
        return outMessageQueue;
    }

    protected void setOutMessageQueue(BlockingQueue<Message> outMessageQueue) {
        this.outMessageQueue = outMessageQueue;
    }

    public BlockingQueue<Message> getInMessageQueue() {
        return inMessageQueue;
    }

    public void setInMessageQueue(BlockingQueue<Message> inMessageQueue) {
        this.inMessageQueue = inMessageQueue;
    }

    public String getAppThreadName() {
        return appThreadName;
    }

    public void setAppThreadName(String name) {
        this.appThreadName = name;
    }

    private class ReaderConvert implements ThreadControllable, Runnable {
        private BlockingQueue<Message> outMessageQueue;

        private Reader reader;

        private String appThreadName;
        private Long appThreadId;

        public Long getAppThreadId() {
            return appThreadId;
        }

        public void setAppThreadId(Long id) {
            this.appThreadId = id;
        }

        public ReaderConvert(BlockingQueue<Message> outMessageQueue, Reader reader) {
            this.outMessageQueue = outMessageQueue;
            this.reader = reader;
        }

        public String getAppThreadName() {
            return appThreadName;
        }

        public void setAppThreadName(String name) {
            this.appThreadName = name;
        }

        @Override
        public void run() {
            System.out.println("Iniciando [" + this.appThreadName + "]");
            BufferedReader in = new BufferedReader(this.reader);
            String line;
            try {

                while ((line = in.readLine()) != null && line.length() != 0) {
                    try {

                        Message message = new Message();
                        message.setMessage(line);
                        message.setThreadId(this.appThreadId);
                        message.setFromThreadId(this.appThreadId);
                        message.setToThreadId(null);
                        message.setType(Message.Type.RAW);
                        // System.out.println("this.id"+this.id);
                        this.outMessageQueue.put(message);

                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
        }

    }

    private class WriterConvert implements ThreadControllable, Runnable {
        private BlockingQueue<Message> inMessageQueue;

        private Writer writer;

        private String appThreadName;

        private Long appThreadId;

        public Long getAppThreadId() {
            return appThreadId;
        }

        public void setAppThreadId(Long id) {
            this.appThreadId = id;
        }

        public WriterConvert(BlockingQueue<Message> inMessageQueue, Writer writer) {
            this.inMessageQueue = inMessageQueue;
            this.writer = writer;
        }

        public String getAppThreadName() {
            return appThreadName;
        }

        public void setAppThreadName(String name) {
            this.appThreadName = name;
        }

        @Override
        public void run() {
            System.out.println("Iniciando [" + this.appThreadName + "]");
            BufferedWriter out = new BufferedWriter(this.writer);
            try {
                Message message = null;
                while (true) {
                    message = inMessageQueue.take();
                    out.write("resultado: ");
                    out.write(message.getMessage());
                    out.write("\n");
                    out.flush();
                }
            } catch (InterruptedException ex) {
                System.out.println("WriterException: " + ex);

            } catch (IOException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
        }

    }

}
